#include "pushWithRtsp.h"
#include "util.h"

namespace sp::Image::Push{

void register_pushWithRtsp(){
    pushWithRtsp::registe();
}

pushWithRtsp::pushWithRtsp(std::vector<std::string> v){
    assert(v.size() == 4);
    name = "pushWithRtsp";
    class_name = "Image-Push-pushWithRtsp";
    data_type_support.push_back(std::pair<std::vector<uint8_t>, uint8_t>({MAT},UNKNOW));
    this->rtspUrl = v[0];
    this->fps = std::stoi(v[1]);
    this->width = std::stoi(v[2]);
    this->height = std::stoi(v[3]);
    this->index = 0;
}

pushWithRtsp::pushWithRtsp(std::string url, int fps, int width, int height){
    name = "pushWithRtsp";
    class_name = "Image-Push-pushWithRtsp";
    data_type_support.push_back(std::pair<std::vector<uint8_t>, uint8_t>({MAT},UNKNOW));
    this->rtspUrl = url;
    this->fps = fps;
    this->width = width;
    this->height = height;
    this->index = 0;
}

pushWithRtsp::pushWithRtsp(){
    name = "pushWithRtsp";
    class_name = "Image-Push-pushWithRtsp";
    this->index = 0;
    data_type_support.push_back(std::pair<std::vector<uint8_t>, uint8_t>({MAT},UNKNOW));
}

pushWithRtsp::~pushWithRtsp(){

}

void pushWithRtsp::registe(){
    Function::Register("Image-Push-pushWithRtsp", createObject);
    Function::Register("Image-Push-pushWithRtsp", createObject2);
}

Function* pushWithRtsp::createObject(std::vector<std::string> params){
    return new pushWithRtsp(params);
}

Function* pushWithRtsp::createObject2(){
    return new pushWithRtsp();
}

void pushWithRtsp::start(){
    // 确保本机环境中有ffmpeg，并且初始化ffmpeg进程
    std::string size = std::to_string(width) + "x" + std::to_string(height);
    std::vector<std::string> arguments = {
        "ffmpeg",
        "-y", "-an",
        "-f", "rawvideo",
        "-vcodec", "rawvideo",
        "-pix_fmt", "bgr24",
        "-s", size,
        "-r", std::to_string(fps),
        "-i", "- ",
        "-c:v", "libx264",
        "-pix_fmt", "yuv420p",
        "-preset", "ultrafast",
        "-f", "rtsp",
        "-rtsp_transport", "tcp",
        rtspUrl
    };
    std::string command = Util::join(arguments, " ");
    std::cout << command << std::endl;
    // 打开ffmpeg子进程，用于音视频处理
    fp = popen(command.c_str(), "w");
    assert(fp != nullptr);
}

bool pushWithRtsp::waitForResource(){
    return true;
}

void pushWithRtsp::process(std::vector<std::vector<Data*>>& data_input, std::vector<Data*>& data_output){
    // 验证输入个数
    assert(batch_size != 0);
    assert(data_input.size() <= batch_size);
    assert(data_input[0].size() == 1);
    // 处理
    for(int i = 0; i < data_input.size(); i++){
        if(data_input[i][0] == DATA_END)
            break;
        // 接收Mat
        Data* d = data_input[i][0];
        short app_id = d->getAppId();
        short flow_id = d->getFlowId();
        short request_id = d->getRequestId();
        cv::Mat* src = new cv::Mat(d->context.mat.rows, d->context.mat.cols, CV_8UC3); 
        src->data = d->context.mat.data;
        src->datastart = d->context.mat.data;
        src->dataend = d->context.mat.data + d->context.mat.rows*d->context.mat.cols*d->context.mat.channels;
        src->datalimit = src->dataend;
        // push到RTSP流
        fwrite(src->data, sizeof(char), src->total()*src->elemSize(), fp);
        // std::cout << "push frame " << width << "*" << height << " : " << index << std::endl;
        // 释放内存
        if(d->subRefCount() == 0){
            executor->free((char*)(d->context.mat.data));
            executor->free((char*)(d));
        }
        char* ptr = executor->malloc(Data_Memory);
        Data* data = new (ptr)Data(app_id, flow_id, request_id, UNKNOW);
        data_output.push_back(data);
        index++;
    }

}

bool pushWithRtsp::releaseResource(){
    return true;
}

void pushWithRtsp::finish(){
    // 关闭子进程
    if(fp != nullptr)
        pclose(fp);
}

void pushWithRtsp::copy(Function* other){
    Function::defaultCopy(other);
    pushWithRtsp* oth = dynamic_cast<pushWithRtsp*>(other);
    this->rtspUrl = oth->rtspUrl;
    this->fps = oth->fps;
    this->height = oth->height;
    this->width = oth->width;
}

};